#include "FileMappingStream.h"

FileMappingStream::FileMappingStream(void):
m_hFile(INVALID_HANDLE_VALUE),
m_hFileMapping(INVALID_HANDLE_VALUE),
m_pFileMapping(NULL),
m_uFileSize(0),
m_uBeginPos(0),
m_uCurrentPos(0),
m_uEndPos(0),
m_uMappingLen(0)
{
}

FileMappingStream::~FileMappingStream(void)
{
	Close();
}

int32 FileMappingStream::Open(ctchar* pcsFilePath, uint64 uFileSize, uint64 uMappingPos /*= 0 * 64 * KB*/, uint64 uMappingLen /*= 0*/, uint32 uMode /* = MODE_READ_ONLY*/, ctchar* pcsShareName /*= NULL*/)
{
	//Set the access mode
	DWORD dwAccessMode  = 0;
	DWORD dwProtected = 0;
	DWORD dwMapViewProtected = 0;

	if(uMode == MODE_READ_ONLY)
	{
		dwAccessMode = GENERIC_READ;
		dwProtected = PAGE_READONLY;
		dwMapViewProtected = FILE_MAP_READ;
	}
	else if(uMode == MODE_READ_WRITE)
	{
		dwAccessMode = GENERIC_READ | GENERIC_WRITE;
		dwProtected = PAGE_READWRITE;
		dwMapViewProtected = FILE_MAP_WRITE;
	}
	else
	{
		dwAccessMode = GENERIC_READ;
		dwAccessMode = PAGE_READONLY;
		dwMapViewProtected = FILE_MAP_READ;
	}

	//Create the file
	if (pcsFilePath == NULL)
	{
		m_hFile = INVALID_HANDLE_VALUE;
	}
	else
	{
		HANDLE m_hFile = ::CreateFile(pcsFilePath , dwAccessMode , FILE_SHARE_READ , 0 , OPEN_ALWAYS , FILE_ATTRIBUTE_NORMAL , NULL);

		if(m_hFile == INVALID_HANDLE_VALUE)
		{
			CloseHandle(m_hFile);

			return E_OPEN_FILE_MAPPING;
		}
	}

	//Create the file mapping
	LARGE_INTEGER fileSize;
	fileSize.QuadPart = uFileSize;

	m_hFileMapping = ::CreateFileMapping(m_hFile, NULL, dwProtected, fileSize.HighPart, fileSize.LowPart, pcsShareName);

	if (m_hFileMapping == INVALID_HANDLE_VALUE)
	{
		CloseHandle(m_hFile);
		CloseHandle(m_hFileMapping);

		return E_FILE_MAPPING;
	}

	//Map the file
	LARGE_INTEGER uBeginPos;
	uBeginPos.QuadPart = uMappingPos;

	m_pFileMapping = (byte*)::MapViewOfFile(m_hFileMapping, dwMapViewProtected, uBeginPos.HighPart, uBeginPos.LowPart, uMappingLen);
	
	if (m_pFileMapping == NULL)
	{
		CloseHandle(m_hFile);
		CloseHandle(m_hFileMapping);

		return R_FAILED;
	}

	//Initial the position and size variable
	m_uFileSize = uFileSize;
	m_uBeginPos = uMappingPos;
	m_uCurrentPos = uMappingPos;

	if (uMappingLen == 0)
	{
		m_uEndPos = uFileSize;
		m_uMappingLen = m_uFileSize - m_uBeginPos;
	}
	else
	{
		m_uMappingLen = uMappingLen;
		m_uEndPos = m_uBeginPos + m_uMappingLen;
	}

	return R_SUCCEED;
}

int32 FileMappingStream::CanRead()
{
	return CheckFileMappingInvalidate();
}

int32 FileMappingStream::CanSeek()
{
	return CheckFileMappingInvalidate();
}

int32 FileMappingStream::CanWrite()
{
	return CheckFileMappingInvalidate();
}

int32 FileMappingStream::HaveEOS()
{
	return CheckFileMappingInvalidate();
}

int32 FileMappingStream::Seek( uint64 position , SEEK_POSITION flag /*= CURRENT*/ )
{
	if (CanSeek() != R_SUCCEED)
	{
		return E_FILESEEK_MAPPING;
	}

	if (flag == BEGIN)
	{
		if (position + m_uBeginPos <= m_uEndPos)
		{
			m_uCurrentPos = m_uBeginPos + position;
		}
		else
		{
			return E_FILESEEK_MAPPING;
		}
	}
	else if (flag == END)
	{
		if (position == 0)
		{
			m_uCurrentPos = m_uEndPos;
		}
		else
		{
			return E_FILESEEK_MAPPING;
		}
	}
	else
	{
		if (position + m_uCurrentPos <= m_uEndPos)
		{
			m_uCurrentPos += position;
		}
		else
		{
			return E_FILESEEK_MAPPING;
		}
	}

	return R_SUCCEED;
}

int32 FileMappingStream::GetPosition( uint64& position )
{
	position = m_uCurrentPos - m_uBeginPos;

	return R_SUCCEED;
}

int32 FileMappingStream::GetSize( uint64& size )
{
	size = m_uMappingLen;

	return R_SUCCEED;
}

int32 FileMappingStream::Read(  byte* block , uint32 blockSize , uint32& fileRead )
{
	if (CanRead() != R_SUCCEED)
	{
		return E_FILEREAD_MAPPING;
	}

	byte* pCurrentPos = m_pFileMapping + m_uCurrentPos - m_uBeginPos;

	memcpy_s(block, blockSize, pCurrentPos, blockSize);

	fileRead = blockSize;

	return R_SUCCEED;
}

int32 FileMappingStream::Write( byte* block , uint32 blockSize , uint32& fileRead )
{
	if (CanWrite() != R_SUCCEED)
	{
		return E_FILEWRITE_MAPPING;
	}

	byte* pCurrentPos = m_pFileMapping + m_uCurrentPos - m_uBeginPos;

	memcpy_s(pCurrentPos, blockSize, block, blockSize);

	fileRead = blockSize;

	return R_SUCCEED;
}

int32 FileMappingStream::EndOfStream()
{
	if (m_uCurrentPos == m_uBeginPos + m_uMappingLen)
	{
		return R_SUCCEED;
	}

	return R_NOT_ENDOFSTREAM;
}

int32 FileMappingStream::Close()
{
	if (m_pFileMapping != NULL)
	{
		UnmapViewOfFile(m_pFileMapping);
		m_pFileMapping = NULL;
	}
	
	if (m_hFile != INVALID_HANDLE_VALUE)
	{
		CloseHandle(m_hFile);
		m_hFile = INVALID_HANDLE_VALUE;
	}
	
	if (m_hFileMapping != INVALID_HANDLE_VALUE)
	{
		CloseHandle(m_hFileMapping);
		m_hFileMapping = INVALID_HANDLE_VALUE;
	}

	return R_SUCCEED;
}

int32 FileMappingStream::Flush()
{
	return R_SUCCEED;
}

int32 FileMappingStream::CheckFileMappingInvalidate()
{
	if (m_pFileMapping == NULL)
	{
		return R_FAILED;
	}

	return R_SUCCEED;
}